#include <sys/param.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "file_mapper.h"

struct file_mapper *
file_mapper_open(struct mediafs_decoder_ctx *decoder_ctx, struct mediafs_encoder_ctx *encoder_ctx)
{	
	struct file_mapper *result;
	int res;

	result = calloc(1, sizeof(*result));
	assert(result != NULL);
	result->decoder_ctx = decoder_ctx;
	result->encoder_ctx = encoder_ctx;

	res = mediafs_encoder_write_header(encoder_ctx, NULL, &result->header_size);
	assert(res == 0);

	result->header = calloc(1, result->header_size);
	assert(result->header != NULL);
	res = mediafs_encoder_write_header(encoder_ctx, result->header, &result->header_size);
	assert(res == 0);

	res = mediafs_encoder_get_stat(encoder_ctx, &result->encoder_stat);
	assert(res == 0);

	return result;
}

size_t
file_mapper_get_mapped_size(struct file_mapper *fmapper)
{
	return (fmapper->header_size + fmapper->encoder_stat.frames_number * fmapper->encoder_stat.frame_size);
}

int
file_mapper_get_mapped_bytes(struct file_mapper *fmapper, size_t offset, size_t size, char *buffer)
{
	struct mediafs_decoder_stat stat;
	char *temp_src_buffer, *temp_dest_buffer;;
	size_t header_chunk_size;
	size_t left_frame, right_frame;

	if (offset < fmapper->header_size) {
		header_chunk_size = MIN(fmapper->header_size - offset, size);
		memcpy(buffer, fmapper->header + offset, header_chunk_size);
		size -= header_chunk_size;
		offset += header_chunk_size;
		buffer += header_chunk_size;
	}
	if (size > 0) {
		memset(&stat, 0, sizeof(stat));
		mediafs_decoder_get_stat(fmapper->decoder_ctx, &stat);

		int decoded_frame_size = stat.num_channels * stat.bits_per_sample / 8;
		left_frame = (offset - fmapper->header_size) / fmapper->encoder_stat.frame_size;
		right_frame = MIN((offset + size) / fmapper->encoder_stat.frame_size + 1, fmapper->encoder_stat.frames_number);

		temp_src_buffer = calloc(1, (right_frame - left_frame) * fmapper->encoder_stat.samples_per_frame * decoded_frame_size);
		mediafs_decoder_decode(fmapper->decoder_ctx, left_frame * fmapper->encoder_stat.samples_per_frame, (right_frame - left_frame) * fmapper->encoder_stat.samples_per_frame, temp_src_buffer);
		
		size_t temp_dest_buffer_size = (right_frame - left_frame) * fmapper->encoder_stat.frame_size;
		temp_dest_buffer = calloc(1, temp_dest_buffer_size);
		mediafs_encoder_encode(fmapper->encoder_ctx, left_frame, right_frame - left_frame, temp_src_buffer, temp_dest_buffer, &temp_dest_buffer_size);

		memcpy(buffer, temp_dest_buffer + (offset - fmapper->header_size) % fmapper->encoder_stat.frame_size, size);
		
		free(temp_src_buffer);
		free(temp_dest_buffer);
	}
	return 0;
}

void
file_mapper_close(struct file_mapper *fmapper)
{
	free(fmapper->header);
	free(fmapper);
}

